import asyncio
import json
import logging
from typing import List

import aiohttp
import time
import re
import xml
import xml.etree.ElementTree as ET
from selenium import webdriver
import undetected_chromedriver as chromedriver

from price_analysis.core.config import settings
from price_analysis.utils.file_support import JsonFileSupport
from price_analysis.data.settings.domain_black_list import black_list

logger = logging.getLogger("main")


class SearchOnline:
    def __init__(self):
        self.logger = logger.getChild("search-online")

    @staticmethod
    async def fetch_url(url: str) -> str:
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                return await response.text()

    @staticmethod
    def __get_url(query: str, page: int = 0) -> str:
        return (
            f"{settings.yandex_url}"
            + f"?folderid={settings.yandex_id_catalog}"
            + f"&apikey={settings.yandex_api_key}"
            + f"&query={query}"
            + f"&page={page}"
        )

    def check_xml(self, data: str) -> str | None:
        if data:
            root = ET.fromstring(data)
            error = root.find(".//error")
            if error:
                self.logger.error(error.text)
                self.logger.info("Данные не получены")
                return None

            self.logger.info("Данные получены успешно")
            return data
        else:
            self.logger.info("Данные не получены")
            return None

    async def __get_request_to_yandex_api(self, query: str, page: int = 0) -> str:
        self.logger.info("Получение результатов поиска")

        url: str = self.__get_url(query, page)
        xml_data = await self.fetch_url(url)

        root = ET.fromstring(xml_data)
        found = root.find(".//found").text
        self.logger.info(f"Получено результатов поиска {int(found)}")
        return xml_data

    def get_URLs_to_parse(self, content: str) -> list[dict] | None:

        self.logger.info("Извлечение списка страниц")
        result = []
        root: xml = ET.fromstring(content)
        data = root.findall(".//doc")
        self.logger.info(f"Получено {len(data)} результатов")

        if data:
            for item in data:
                page_data = {
                    "url": item.find("url").text,
                    "domain": item.find("domain").text,
                    "offer_info": json.loads(item.find("properties")[0].text),
                }
                price: int = self.__get_price(page_data["offer_info"])

                if price and (page_data["domain"] not in black_list):
                    page_data["price"] = price
                    result.append(page_data)

                if len(result) > 2:
                    self.logger.info(f"Найдено {len(result)} результ. с ценами")
                    return result

        self.logger.info("Предложений с ценами в кол-ве 3 шт. не найдено")
        return None

    @staticmethod
    def __get_price(info: dict) -> int | None:
        if isinstance(info, dict):
            for key in info:
                if "price" in key.lower():
                    return int(info[key])
        return None

    async def search_item_price_online(self, item: dict) -> dict | None:
        self.logger.info(f"Поиск цены на позицию {item['Description']}")

        query: str = (
            f'{item["Index"]}, {item["Model"]}, {item["Provider"]}, {item["Description"]}'
        )
        ya_response = await self.__get_request_to_yandex_api(query=query)

        if not ya_response:
            return None

        urls_to_parse = self.get_URLs_to_parse(ya_response)

        if not urls_to_parse:
            return None

        item["prices_data"] = []

        dir_name = re.sub(r"[^a-zA-Zа-яА-Я0-9-]", "", item["Model"])
        file_path = f"{settings.page_image_path}/{dir_name}"

        for url_item in urls_to_parse:

            filename = re.sub(r"[^a-zA-Zа-яА-Я0-9]", "_", url_item["domain"])
            full_file_path = f"{file_path}_{filename}.png"
            url = url_item["url"]
            price_data = {
                "url": url,
                "domain": url_item["domain"],
                "price": url_item["price"],
                "img_path": full_file_path,
            }

            try:
                with chromedriver.Chrome() as driver:
                    driver.get(url)
                    time.sleep(15)
                    self.logger.info(f"Загрузка скриншота {url} ожидание 15 сек.")
                    driver.save_screenshot(full_file_path)
                    item["prices_data"].append(price_data)
            except Exception as err:
                self.logger.error(f"Ошибка создания скриншота {url}, {err}")

        return item

    @staticmethod
    def __add_id(content: list[dir]) -> list[dir]:
        return [{"id": i + 1, **item} for i, item in enumerate(content)]

    async def get_price_and_images(self, data: list[dict]) -> None:
        self.logger.info(f"Начат поиск цен и сохранение скриншотов")

        full_path_to_report = f"{settings.main_report_path}/main_report.json"
        full_path_to_request_offer = f"{settings.ocr_data_path}/to_request_offer.json"

        to_report = JsonFileSupport(full_path_to_report)
        to_request_offer = JsonFileSupport(full_path_to_request_offer)

        if to_report.check_file_exists():
            self.logger.info(f"Имеется файл {full_path_to_report}")
            self.logger.info(f"Пропускаю поиск онлайн, загружаю имеющиеся данные")
            return

        items_with_price_and_img = []
        items_without_price = []

        try:
            for row in data:
                item_with_price = await self.search_item_price_online(row.copy())

                if item_with_price:
                    items_with_price_and_img.append(item_with_price)
                else:
                    items_without_price.append(row.copy())

                await asyncio.gather(
                    to_report.write_json_file(items_with_price_and_img),
                    to_request_offer.write_json_file(items_without_price),
                )

            items_with_price_and_img = self.__add_id(items_with_price_and_img)
            items_without_price = self.__add_id(items_without_price)

            self.logger.info(f"Запись результатов")
            await asyncio.gather(
                to_report.write_json_file(items_with_price_and_img),
                to_request_offer.write_json_file(items_without_price),
            )

        except Exception as err:
            self.logger.error(
                f"Основной блок цикла - Произошла ошибка во время выполнения: {err}"
            )
